package org.onion.pattern.event.spring.io;

import jakarta.annotation.PostConstruct;
import lombok.Setter;
import org.apache.commons.lang3.ObjectUtils;
import org.onion.pattern.event.io.EventDeserializer;
import org.onion.pattern.event.io.EventSerializer;
import org.onion.pattern.event.io.codec.JacksonEventCodec;
import org.onion.pattern.event.io.codec.ProtobufEventCodec;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

@Component
public class EventCodecs implements BeanPostProcessor {

    @Autowired(required = false)
    private Map<String, EventDeserializer> deserializers;

    @Autowired(required = false)
    private Map<String, EventSerializer> serializers;

    @Autowired
    @Setter
    private ApplicationContext applicationContext;

    @PostConstruct
    void init() {
        this.deserializers = ObjectUtils.defaultIfNull(this.deserializers, new ConcurrentHashMap<>());
        this.deserializers.put(JacksonEventCodec.INSTANCE.type().toLowerCase(), JacksonEventCodec.INSTANCE);
        this.deserializers.put(ProtobufEventCodec.INSTANCE.type().toLowerCase(), ProtobufEventCodec.INSTANCE);

        this.serializers = ObjectUtils.defaultIfNull(this.serializers, new ConcurrentHashMap<>());
        this.serializers.put(JacksonEventCodec.INSTANCE.type().toLowerCase(), JacksonEventCodec.INSTANCE);
        this.serializers.put(ProtobufEventCodec.INSTANCE.type().toLowerCase(), ProtobufEventCodec.INSTANCE);
    }

    public EventDeserializer deserialize(String type) {
        return this.deserializers.get(type.toLowerCase());
    }

    public EventSerializer serialize(String type) {
        return this.serializers.get(type.toLowerCase());
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        if (bean instanceof EventDeserializer) {
            EventDeserializer deserializer = (EventDeserializer) bean;
            if (!deserializers.containsKey(deserializer.type().toLowerCase())) {
                deserializers.put(deserializer.type().toLowerCase(), deserializer);
            }
            return bean;
        }
        if (bean instanceof EventSerializer) {
            EventSerializer serializer = (EventSerializer) bean;
            if (!serializers.containsKey(serializer.type().toLowerCase())) {
                serializers.put(serializer.type().toLowerCase(), serializer);
            }
            return bean;
        }
        if (bean == this) {
            Map<String, EventDeserializer> deserializerMap = applicationContext.getBeansOfType(EventDeserializer.class);
            for (EventDeserializer value : deserializerMap.values()) {
                if (deserializers.containsKey(value.type().toLowerCase())) {
                    continue;
                }
                deserializers.put(value.type().toLowerCase(), value);
            }

            Map<String, EventSerializer> serializerMap = applicationContext.getBeansOfType(EventSerializer.class);
            for (EventSerializer value : serializerMap.values()) {
                if (serializers.containsKey(value.type().toLowerCase())) {
                    continue;
                }
                serializers.put(value.type().toLowerCase(), value);
            }
        }
        return bean;
    }
}
